
#include <stdint.h>
#include <stdlib.h>

#include "compiler.h"
#include "hcb_comn.h"


/**/
struct irqaction {
	uint32_t  stats;
	irq_handler_t	  handler;
	unsigned int    flags;
	const char *    name;
	void *          dev;
};


/**/
static struct irqaction  irq_act[32];

/* intc, 0-31 */
void  arch_irq_enable( int irq )
{
	uint32_t  temp;

	/**/
	temp = read_reg( BASE_AP_INTC + INTC_ENABLE_L );
	temp = temp | (1 << irq);
	write_reg( BASE_AP_INTC + INTC_ENABLE_L, temp );

	/**/
	temp = read_reg( BASE_AP_INTC + INTC_MASK_L );
	temp = temp & ~(1 << irq);
	write_reg( BASE_AP_INTC + INTC_MASK_L, temp );

	/**/
	return;
}


void  arch_irq_disable( int irq )
{
	uint32_t  temp;
	
	/**/
	temp = read_reg( BASE_AP_INTC + INTC_MASK_L );
	temp = temp & ~(1<<irq);
	write_reg( BASE_AP_INTC + INTC_MASK_L, temp );

	/**/
	temp = read_reg( BASE_AP_INTC + INTC_ENABLE_L );
	temp = temp & ~(1<<irq);
	write_reg( BASE_AP_INTC + INTC_ENABLE_L, temp );

	/**/
	return;
}


int  arch_irq_process( void )
{
	int  iret = 0;
	uint32_t  temp;
	uint32_t  irq;

	/* read intc finals */
	temp = read_reg( BASE_AP_INTC + INTC_FINALS );

	while ( temp != 0 )
	{
		/* count lead zero */
		irq = __builtin_clz( temp );
		irq = 31 - irq;
		temp = temp ^ (1ul << irq);
    	
		/**/
		irq_act[irq].stats += 1;
		if ( NULL != irq_act[irq].handler )
		{
			/**/
			iret += irq_act[irq].handler( irq, irq_act[irq].dev );
		}
		else
		{
			/* error, clear, disable and statics. */
			arch_irq_disable( irq );
		}
	}

	/**/
	return iret;
}


/**/
int  arch_irq_request( int irq, irq_handler_t handler, uint32_t flags, const char *name, void * pdev )
{
	if ( (irq < 0) || (irq >= 32) )
	{
		return 1;
	}

	/**/
	irq_act[irq].flags = flags;
	irq_act[irq].name = name;
	irq_act[irq].dev = pdev;

	/* dsb? */
	irq_act[irq].handler = handler;

	/* flags? */
	
	/**/
	return 0;
}


void  arch_irq_free( int irq )
{
	if ( (irq < 0) || (irq >= 16) )
	{
		return;
	}

	/* disable and clear */
	arch_irq_disable( irq );
	irq_act[irq].handler = NULL;
	
	/**/
	return;
}


void  arch_intc_init( void )
{
	int  i;
	
	/**/
	for ( i=0; i<32; i++ )
	{
		irq_act[i].stats = 0;
		irq_act[i].handler = NULL;
	}
	
	/**/
	write_reg( BASE_AP_INTC + INTC_ENABLE_L, 0 );
	write_reg( BASE_AP_INTC + INTC_ENABLE_H, 0 );
	write_reg( BASE_AP_INTC + INTC_MASK_L, 0xFFFFFFFF );
	write_reg( BASE_AP_INTC + INTC_MASK_H, 0xFFFFFFFF );
	return;
}


